/*
 * RenderTests.cpp
 *
 *  Created on: 09.08.2011
 *      Author: Blinov Kirill
 *      Copyright: LGPL
 */

#include <iostream>
#include <string.h>
#include <cstddef>
#include <math.h>
#include <stdlib.h>
#include <boost/thread.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <SDL/SDL.h>

#include "Visualisator/CVisualisator.h"
#include "Visualisator/Camera.h"

#include "Manager Resource/IUnit.h"
#include "Manager Resource/UnitFabric.h"

#include "GUI/GUIInit.h"

#include "Constants.h"
#include "RenderTests.h"
#include "StructSource.h"
#include "CBadPhysics.h"

struct SmdModel
{
	STANDART_VERTEX                *vertices;
	GLushort                       *indexes;
	UINT                            vertexCount;
	GLsizei                         triangleCount;
	std::vector<TEXTURE_DESCRIPTOR> textures;

	SmdModel():vertexCount(0),triangleCount(0){};
	virtual ~SmdModel(){
							if ( NULL != vertices ) delete[] vertices;
							if ( NULL != indexes ) delete[] indexes;
					   };
};

int            IncomeNumber;
volatile bool  Condition;

TThreadSafeQueue<FULL_FRAME_DATA*> FrameQueue;
TThreadSafeQueue<INPUT_MESSAGE*>   InputMessageQueue;
boost::thread                     *RenderRun;
CBadPhysics                        PhysicsEngine;

CGameCamera                MainCamera; //Default parametrs are suitable
std::vector<IUnit*>        GameUnitsArray;
std::vector<GUI_ELEMENT*>  GuiElementsArray;
glm::mat4                  ProjectionMatrix;
GLuint                     VertexArrayId;
RENDER_MODEL_INFO          GuiDummy;
USHORT                     PrevLeftButtonState = MOUSE_KEY_UP;
bool                       DrawMeny = false;
UINT                       FirstShipID;
UINT                       SecondShipID;
float                      ShipsForces[2];

CVisualisator* Visualisator;
CCheckBox*     CheckBox1;

CManagerGUI Manager;
const std::string UNIT_TEMPLATE_NAME = "SimpleHeavyJet";
const std::string UNIT_TEMPLATE_NAME2 = "SimpleIsland";
const std::string WATER_TEMPLATE_NAME = "SimpleWater";
const std::string UNIT_NAME = "FirstJet";
const std::string UNIT_NAME2 = "FirstIsland";
const float SCALE_SUBMARINE_FACTOR = 0.03f;
const float SCALE_ISLAND_FACTOR = 0.05f;
const float SCALE_WATER_FACTOR = 100.0f;
const float WATER_SHIFT_STEP = 0.01;

const USHORT KEY_UP    = 0x111;
const USHORT KEY_DOWN  = 0x112;
const USHORT KEY_RIGHT = 0x113;
const USHORT KEY_LEFT  = 0x114;

float
WaterOffsetCalculation()
{
	static float Shift = 0;
	Shift = ( Shift < (1.0 - WATER_SHIFT_STEP) ) ? ( Shift + WATER_SHIFT_STEP ) : 0;
	return Shift;
}

void
CalculateGabarits( const SmdModel& Model, const float ScaleFactor, glm::vec4 &outSphere, glm::vec3 &outFirstCorner, glm::vec3 &outSecondCorner )
{
	glm::vec3 tmpOutFirstCorner = glm::vec3( Model.vertices[0].position.x, Model.vertices[0].position.y, Model.vertices[0].position.z ); // minimal values
	glm::vec3 tmpOutSecondCorner =  tmpOutFirstCorner; // maximal values
	outSphere = glm::vec4(0);

	//Find dimensions
	for ( UINT i = 1; i < Model.vertexCount; i++ )
	{
		STANDART_VERTEX WorkVertex = Model.vertices[i];
		if ( WorkVertex.position.x > tmpOutSecondCorner.x )
		{
			tmpOutSecondCorner.x = WorkVertex.position.x;
		}
		else if ( WorkVertex.position.x < tmpOutFirstCorner.x )
		{
			tmpOutFirstCorner.x = WorkVertex.position.x;
		}

		if ( WorkVertex.position.y > tmpOutSecondCorner.y )
		{
			tmpOutSecondCorner.y = WorkVertex.position.y;
		}
		else if ( WorkVertex.position.y < tmpOutFirstCorner.y )
		{
			tmpOutFirstCorner.y = WorkVertex.position.y;
		}

		if ( WorkVertex.position.z > tmpOutSecondCorner.z )
		{
			tmpOutSecondCorner.z = WorkVertex.position.z;
		}
		else if ( WorkVertex.position.z < tmpOutFirstCorner.z )
		{
			tmpOutFirstCorner.z = WorkVertex.position.z;
		}
	}

	tmpOutSecondCorner = tmpOutSecondCorner * ScaleFactor;
	tmpOutFirstCorner  = tmpOutFirstCorner * ScaleFactor;
	outFirstCorner = tmpOutFirstCorner;
	outSecondCorner = tmpOutSecondCorner;

	glm::vec3 Radius = (( outFirstCorner + outSecondCorner ) / 2.0f);
	outSphere = glm::vec4( Radius, glm::length( Radius - outFirstCorner ) );
}

STANDART_VERTEX
ParseVertex( const char* VertString )
{
	STANDART_VERTEX Result;
	int TempNull;
	float TempNullF;

	sscanf( VertString, "%d %f %f %f %f %f %f %f %f %d %d %f", &TempNull,
			&Result.position.x, &Result.position.y, &Result.position.z,
			&Result.normal.x, &Result.normal.y, &Result.normal.z,
			&Result.texture.x, &Result.texture.y,
			&TempNull, &TempNull, &TempNullF);

	return Result;
}

SmdModel*
CompressBuffersToModel( const STANDART_VERTEX *Vertices, const UINT VertCount )
{
	SmdModel* Result = new SmdModel();
	Result->triangleCount = VertCount / 3;
	Result->indexes = new GLushort[VertCount];

	memset( (void*)( Result->indexes ), 0xFFFF, size_t( sizeof(GLushort) * VertCount ) );

	STANDART_VERTEX CompressedVertices[VertCount];
	GLushort CurrentIndex = 0;

	for ( UINT i = 0; i < VertCount; i++ )
	{
        if ( 0xFFFF == Result->indexes[i] )
        {
        	STANDART_VERTEX tmpVertex = Vertices[i];
        	Result->indexes[i] = CurrentIndex;
        	for ( UINT j = (i+1); j < VertCount; j++ )
        	{
        		if (( Vertices[j].position.x == tmpVertex.position.x ) &&
        			( Vertices[j].position.y == tmpVertex.position.y ) &&
        			( Vertices[j].position.z == tmpVertex.position.z ))
        		{
        			Result->indexes[j] = CurrentIndex;
        		}
        	}
        	CompressedVertices[CurrentIndex] = tmpVertex;
        	CurrentIndex++;
        }
	}

	Result->vertices = new STANDART_VERTEX[CurrentIndex];
	Result->vertexCount = CurrentIndex;

	memcpy( (void*)Result->vertices, (void*)CompressedVertices, sizeof(STANDART_VERTEX) * CurrentIndex );

	return Result;
}

SmdModel*
LoadSMDModel( const char* FileName )
{
	SmdModel* Result = NULL;
	std::vector<TEXTURE_DESCRIPTOR> Textures;

	char buffer[1024];
	STANDART_VERTEX* TempVertex = NULL;
	std::string      TextureNameCash = "";
	UINT             CashedStringNumber;
	UINT             TriangleCount = 0;

	Textures.reserve(3);
	FILE *file = fopen(FileName,"rt");

	if (file != NULL)
	{
		while (fgets(buffer,1023,file))
		{
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
			if ((strcmp(buffer,"triangles") == 0) && (0 == TriangleCount))
				while (fgets(buffer,1023,file))
				{
					buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
					if(strcmp(buffer,"end") == 0)
					{
						TempVertex = new STANDART_VERTEX[TriangleCount*3];
						rewind(file);
						break;
					}

					fgets(buffer,1023,file);
					fgets(buffer,1023,file);
					fgets(buffer,1023,file);
					TriangleCount++;
				}
			//--------------------------------------
			if ( (strcmp(buffer,"triangles") == 0) && ( TempVertex != NULL ) )
			{
				for (UINT i = 0; i < TriangleCount ;i++)
				{
					fgets(buffer,1023,file);

					if ( strcmp( buffer, TextureNameCash.c_str() ) != 0 )
					{
						bool TextureNameFound = false;

						for ( UINT i = 0; i < Textures.size(); i++ )
						{
							if ( strcmp( buffer, Textures[i].name.c_str() ) == 0 )
							{
								TextureNameCash = Textures[i].name;
								CashedStringNumber = i;
								TextureNameFound = true;
								break;
							}
						}

						if ( !TextureNameFound )
						{
							Textures.push_back( TEXTURE_DESCRIPTOR( std::string( buffer ), (short)Textures.size() ) );
							TextureNameCash.assign( buffer );
							CashedStringNumber = Textures.size() - 1;
						}
					}

					for ( int j = 0; j < 3; j++ )
					{
						fgets(buffer,1023,file);
						TempVertex[i*3+j] = ParseVertex( buffer );
						TempVertex[i*3+j].bonesIndexesAndTextureNumber.w = CashedStringNumber;
					}
				}

				Result = CompressBuffersToModel( TempVertex, TriangleCount * 3 );
				Result->textures = Textures;
			}
		}
	}
	else
	{
		fprintf( stderr, "ERROR!! File %s not found...\n", FileName );
	}

	if ( NULL != TempVertex )
	{
	    delete[] TempVertex;
	}

	fclose(file);
	file = NULL;
	return Result;
}

void
FirstRenderTest()
{
	fprintf( stderr, "--FirstRenderTest\n" );
	while( Condition )
	{
		std::vector<GAME_OBJECT*> GameObjectsArray;

		//PhysicsEngine.ApplyImpulse( 1, glm::vec3( 0.0, 0.0, 0.0 ) );
		//PhysicsEngine.MoveRotate( 1, glm::vec3( 0.0, 0.0, 1.0 ) );

		//Run physics calculations
		PhysicsEngine.Simulate();
		//Allocate memory for GAME_OBJECT-s
		GameObjectsArray.reserve( GameUnitsArray.size() );
		UINT Counter = 0;

		for ( std::vector<IUnit*>::const_iterator BeginIt = GameUnitsArray.begin(),
			  FinishIt = GameUnitsArray.end();
			  BeginIt != FinishIt; ++BeginIt )
		{
			IUnit* CurrentUnit = *BeginIt.base();
			GAME_OBJECT* TmpGameObject = CurrentUnit->GetVisualInfo();

			if ( ( Counter == FirstShipID ) || ( Counter == SecondShipID ) )
			{
				TmpGameObject->worldMatrix = PhysicsEngine.GetObjectMatrix( Counter );

				glm::vec4    Force( -1.0f, 0.0f, 0.0f, 1.0f );
				Force = Force * glm::inverse(TmpGameObject->worldMatrix);
				Force = Force * ShipsForces[Counter];

				glm::vec3 ResultForce( Force.x, Force.y, Force.z );
				PhysicsEngine.ApplyForce( Counter, ResultForce );
			}

			GameObjectsArray.push_back( TmpGameObject );

			Counter++;
		}

		FULL_FRAME_DATA *FrameData =  new FULL_FRAME_DATA();
		FrameData->guiDummy =             &GuiDummy;
		FrameData->vertexArrayId =        VertexArrayId;
		FrameData->gameObjectsArray =     GameObjectsArray; //FIXME It's A BIG BUG...
		FrameData->camerasArray     =     std::vector<GAME_CAMERA*>( 1, MainCamera.GetCurrentCamera() );

		if  ( DrawMeny )
		{
			FrameData->guiElementsArray = Manager.Draw();
		}

		FrameQueue.Send( FrameData );
		boost::this_thread::sleep( boost::posix_time::milliseconds( RENDER_DEFAULT_WAIT_TIME ) );

		UINT CurrentUnitToControl = Manager.GetControlParam( "CCheckBox1", PARAM_CHECKED ) ? SecondShipID : FirstShipID;

		//Process messages
		INPUT_MESSAGE* InputMessage;
		while ( InputMessageQueue.Receive( &InputMessage ) )
		{
			if ( InputMessage->terminateApplication )
			{
				Condition = false;
			}

			if ( ( InputMessage->mouseDelta.x != 0 ) ||
				 ( InputMessage->mouseDelta.y != 0 ) ||
				 ( PrevLeftButtonState != InputMessage->mouseKeys[MOUSE_BUTTON_LEFT] ) )
			{
				glm::vec2 t;
				t.x = float(InputMessage->mousePosition.x) / float(WINDOW_WIDTH);
				t.y = ( 1.0f - float(InputMessage->mousePosition.y) / float(WINDOW_HEIGHT) );
				PrevLeftButtonState = InputMessage->mouseKeys[MOUSE_BUTTON_LEFT];
				Manager.EventMouse( PrevLeftButtonState, t );
			}

			for ( std::vector<USHORT>::const_iterator it = InputMessage->keys.begin(),
				  end = InputMessage->keys.end();
				  it != end; ++it)
			{
				USHORT Key = *it.base();
				switch (Key)
				{
					break;case 'q':
					{
						Condition = false;
					}
					break;case 'w':
					{
						MainCamera.Translate( -0.1, 0.0, 0.0 );
					}
					break;case 's':
					{
						MainCamera.Translate( 0.1, 0.0, 0.0 );
					}
					break;case 'a':
					{
						MainCamera.Translate( 0.0, 0.1, 0.0 );
					}
					break;case 'd':
					{
						MainCamera.Translate( 0.0, -0.1, 0.0 );
					}
					break;case 'e':
					{
						DrawMeny = !DrawMeny;
					}
					break;case 'z':
					{
						MainCamera.RotateCenterAroundEye( 0.0, 0.0, 1.0 );
						//fprintf( stderr, "orienatation: +1 angel, z + 1.0 \n" );
						//_Z += 1.0;
					}
					break;case 'x':
					{
						MainCamera.RotateCenterAroundEye( 0.0, 0.0, -1.0 );
						//fprintf( stderr, "orienatation: +1 angel, z - 1.0 \n" );
						//_Z += -1.0;
					}
					break;case 'c':
					{
						MainCamera.RotateCenterAroundEye( 0.0, 1.0, 0.0 );
						//fprintf( stderr, "orienatation: +1 angel, y + 1.0 \n" );
						//_Y += 1.0;
					}
					break;case 'v':
					{
						MainCamera.RotateCenterAroundEye( 0.0, -1.0, 0.0 );
						//fprintf( stderr, "orienatation: +1 angel, y - 1.0 \n" );
						//_Y += -1.0;
					}
					break;case 'b':
					{
						MainCamera.RotateCenterAroundEye( 1.0, 0.0, 0.0 );
						//fprintf( stderr, "orienatation: +1 angel, x + 1.0 \n" );
						//_X += 1.0;
					}
					break;case 'n':
					{
						MainCamera.RotateCenterAroundEye( -1.0, 0.0, 0.0 );
						//fprintf( stderr, "orienatation: +1 angel, x - 1.0 \n" );
						//_X += -1.0;
					}
					break;case KEY_UP:
					{
						if ( ShipsForces[CurrentUnitToControl] < 5. )
						{
							ShipsForces[CurrentUnitToControl] += 1.0;
						}
					}
					break;case KEY_DOWN:
					{
						if ( ShipsForces[CurrentUnitToControl] > -5. )
						{
							ShipsForces[CurrentUnitToControl] -= 1.0;
						}
					}
					break;case KEY_LEFT:
					{
						glm::vec3 tmpPoint = GameUnitsArray[CurrentUnitToControl]->GetVisualInfo()->sphereCenter -
								                 glm::vec3( 0.0, 1.0, 0.0 );
						glm::vec4 ForcePoint( tmpPoint, 1. );
						ForcePoint = ForcePoint * PhysicsEngine.GetObjectMatrix( CurrentUnitToControl );

						glm::vec4    Force( 0.0, 1.0, 0., 1.0 );
						Force = Force * PhysicsEngine.GetObjectMatrix( CurrentUnitToControl );
						Force = Force * 5.;

						glm::vec3 ResultForce( Force.x, Force.y, Force.z );
						glm::vec3 ResultPoint( ForcePoint.x, ForcePoint.y, ForcePoint.z );
						PhysicsEngine.ApplyForce( CurrentUnitToControl, ResultForce, ResultPoint );
						//applyTorque
					}
					break;case KEY_RIGHT:
					{
						glm::vec3 tmpPoint = GameUnitsArray[CurrentUnitToControl]->GetVisualInfo()->sphereCenter -
								                 glm::vec3( 0.0, 1.0, 0.0 );
						glm::vec4 ForcePoint( tmpPoint, 1. );
						ForcePoint = ForcePoint * PhysicsEngine.GetObjectMatrix( CurrentUnitToControl );

						glm::vec4    Force( 0.0, -1.0, 0., 1.0 );
						Force = Force * PhysicsEngine.GetObjectMatrix( CurrentUnitToControl );
						Force = Force * 5.;

						glm::vec3 ResultForce( Force.x, Force.y, Force.z );
						glm::vec3 ResultPoint( ForcePoint.x, ForcePoint.y, ForcePoint.z );
						PhysicsEngine.ApplyForce( CurrentUnitToControl, ResultForce, ResultPoint );
					}
					break;default:
					{
						//fprintf( stderr, "X: %f, Y: %f, Z: %f\n", _X, _Y, _Z );
					}
					break;
				}
			}
			delete InputMessage;
		}
	}

	Visualisator->StopVisualisation();
}

int
Mmain( )
{
	memset( ShipsForces, 0, sizeof(ShipsForces) );
	//Fill GUI dummy
	fprintf( stderr, "Start execution test\n" );
	fprintf( stderr, "Start visualisator component tests\n" );

	//Setup dimension
	Visualisator = new CVisualisator();
	SCREEN_DIMENSION Dimension;
	Dimension.width = WINDOW_WIDTH;
	Dimension.height = WINDOW_HEIGHT;
	Dimension.multisampleBufferCount = 4;
	Dimension.sdlFlags = SDL_HWSURFACE | SDL_OPENGL;

	fprintf( stderr, "Init window\n" );
	//Init window
	Visualisator->SetDimension( Dimension );
	Visualisator->ConfigVisualisation(  );

	CManagerResource* MResource;
	MResource = new CManagerResource();

	//-------------GUI---------------------------
	//CManagerGUI Manager;
	Manager.Init( "./ShablonsGUI/TestShablon.txt", "./ShablonsGUI/MapGUI.txt" );

	glm::vec2 f;
	CheckBox1 = Manager.Shablon->ShablonCheckBox[0];
	CheckBox1 = new CCheckBox( *( CheckBox1 ) );
	CheckBox1->SetName("CheckBox1");
	f.x = 0.25;
	f.y = 0.1;
	CheckBox1->SetPosition(f);

	CTreeIterator* Iter = Manager.OwnerIterator->GetIterator();
	Manager.AddControl("", CheckBox1);
	
	//TestInit();
	fprintf( stderr, "Load model and shaders\n" );
	SmdModel* SubmarineSmdModel = LoadSMDModel("./models/tanker.SMD");
	SmdModel* IslandSmdModel = LoadSMDModel("./models/island.SMD");
	SmdModel* SmdGui      = LoadSMDModel("./models/dummy.SMD");
	SmdModel* WaterSmdModel      = LoadSMDModel("./models/sea.SMD");

	//Load common Shader
	GLuint VertexShaderName = Visualisator->SetUpShader( "./shaders/VertexShader.vert" );
	GLuint FragmentShaderName = Visualisator->SetUpShader( "./shaders/FragmentShader.frag" );

	fprintf(stdout, "Shaders names: %u %u\n", VertexShaderName, FragmentShaderName );
	IShaderProgram *SimpleShaderName;
	if ( Visualisator->LinkShaderProgramm( VertexShaderName, FragmentShaderName, &SimpleShaderName ) )
	{
		Visualisator->DefineShaderInputParam<SHADER_INPUT_COMMON_VALUES>( SimpleShaderName, "MVP", SHADER_INPUT_MVP_MATRIX );
		Visualisator->DefineShaderInputParam<SHADER_INPUT_COMMON_VALUES>( SimpleShaderName, "Model", SHADER_INPUT_MODEL_MATRIX );
		Visualisator->DefineShaderInputParam<SHADER_INPUT_COMMON_VALUES>( SimpleShaderName,
																		  "LightDir",
																		   SHADER_INPUT_LIGHT_DIRECTION );
		Visualisator->DefineShaderInputParam<SHADER_INPUT_COMMON_VALUES>( SimpleShaderName, "Diffuse", SHADER_INPUT_DIFFUSE_MAP );
	}
	else
	{
		fprintf( stderr, "Linking shader is failed\n" );
		return 0;
	}

	//Load shader for Water
	VertexShaderName = Visualisator->SetUpShader( "./shaders/WaterVertexShader.vert" );
	//FragmentShaderName = Visualisator->SetUpShader( "./shaders/WaterFragmentShader.frag" );
	fprintf(stdout, "Shaders names: %u %u\n", VertexShaderName, FragmentShaderName );
	IShaderProgram *WaterShaderName;
	if ( Visualisator->LinkShaderProgramm( VertexShaderName, FragmentShaderName, &WaterShaderName ) )
	{
		Visualisator->DefineShaderInputParam<SHADER_INPUT_COMMON_VALUES>( WaterShaderName, "MVP", SHADER_INPUT_MVP_MATRIX );
		Visualisator->DefineShaderInputParam<SHADER_INPUT_COMMON_VALUES>( WaterShaderName, "Model", SHADER_INPUT_MODEL_MATRIX );
		Visualisator->DefineShaderInputParam<SHADER_INPUT_COMMON_VALUES>( WaterShaderName,
																		  "LightDir",
																		  SHADER_INPUT_LIGHT_DIRECTION );
		Visualisator->DefineShaderInputParam<float>( WaterShaderName, "TextureOffsetX", boost::bind( WaterOffsetCalculation ) );
		Visualisator->DefineShaderInputParam<SHADER_INPUT_COMMON_VALUES>( WaterShaderName, "Diffuse", SHADER_INPUT_DIFFUSE_MAP );
	}
	else
	{
		fprintf( stderr, "Linking GUI shaders is failed\n" );
		return 0;
	}

	//Load shader for GUI
	VertexShaderName = Visualisator->SetUpShader( "./shaders/GuiVertexShader.vert" );
	FragmentShaderName = Visualisator->SetUpShader( "./shaders/GuiFragmentShader.frag" );
	fprintf(stdout, "Shaders names: %u %u\n", VertexShaderName, FragmentShaderName );
	IShaderProgram *GuiShaderName;
	if ( Visualisator->LinkShaderProgramm( VertexShaderName, FragmentShaderName, &GuiShaderName ) )
	{
		Visualisator->DefineShaderInputParam<SHADER_INPUT_COMMON_VALUES>( GuiShaderName, "MVP", SHADER_INPUT_MVP_MATRIX );
		Visualisator->DefineShaderInputParam<SHADER_INPUT_COMMON_VALUES>( GuiShaderName, "Diffuse", SHADER_INPUT_DIFFUSE_MAP );
	}
	else
	{
		fprintf( stderr, "Linking GUI shaders is failed\n" );
		return 0;
	}

	fprintf( stderr, "Create Game object1\n" );
	//Create Game object
	RENDER_MODEL_INFO* SubmarineModel = new RENDER_MODEL_INFO( SimpleShaderName, SubmarineSmdModel->vertexCount,
													  std::vector<GLushort> ( SubmarineSmdModel->indexes, SubmarineSmdModel->indexes +
															                  ( SubmarineSmdModel->triangleCount * 3 ) ),
													  SubmarineSmdModel->textures
												    );
	RENDER_MODEL_INFO* IslandModel = new RENDER_MODEL_INFO( SimpleShaderName, IslandSmdModel->vertexCount,
													  std::vector<GLushort> ( IslandSmdModel->indexes, IslandSmdModel->indexes +
															                  ( IslandSmdModel->triangleCount * 3 ) ),
													  IslandSmdModel->textures
												    );
	RENDER_MODEL_INFO* WaterModel = new RENDER_MODEL_INFO( WaterShaderName, WaterSmdModel->vertexCount,
													  std::vector<GLushort> ( WaterSmdModel->indexes, WaterSmdModel->indexes +
															                  ( WaterSmdModel->triangleCount * 3 ) ),
													  WaterSmdModel->textures
												    );
	fprintf( stderr, "Create Game object after\n" );

	GuiDummy.shaderProgram = GuiShaderName;
	GuiDummy.vertexCount = SmdGui->vertexCount;
	GuiDummy.indexes = std::vector<GLushort> ( SmdGui->indexes, SmdGui->indexes + ( SmdGui->triangleCount * 3 ) );
	GuiDummy.indexType = GL_UNSIGNED_SHORT;
	GuiDummy.primitiveType = GL_TRIANGLES;

	glm::mat4 SubmarineInstanceMatrix = glm::scale( glm::mat4(1.0f),
			                                        glm::vec3( SCALE_SUBMARINE_FACTOR, SCALE_SUBMARINE_FACTOR, SCALE_SUBMARINE_FACTOR ) );
	glm::mat4 IslandInstanceMatrix = glm::scale( glm::mat4(1.0f),
			                                     glm::vec3( SCALE_ISLAND_FACTOR, SCALE_ISLAND_FACTOR, SCALE_ISLAND_FACTOR ) );
	glm::mat4 WaterInstanceMatrix = glm::scale( glm::mat4(1.0f),
			                                     glm::vec3( SCALE_WATER_FACTOR, SCALE_WATER_FACTOR, SCALE_WATER_FACTOR ) );

	GLuint TextureSubmarine = MResource->SetUpTexture2D( std::vector<std::string>( 1, "./textures/tanker.dds" ) );
	GLuint TextureIsland = MResource->SetUpTexture2D( std::vector<std::string>( 1, "./textures/island.dds" ) );
	GLuint TextureWater = MResource->SetUpTexture2D( std::vector<std::string>( 1, "./textures/water.dds" ) );

	TEXTURE_DESCRIPTOR TmpTextureDescriptor;
	TmpTextureDescriptor.number = 0;
	TmpTextureDescriptor.textureGLid = TextureSubmarine;
	TmpTextureDescriptor.textureType = GL_TEXTURE_2D_ARRAY;

	GAME_SUBOBJECT_INSTANCE *ModelInstance = new GAME_SUBOBJECT_INSTANCE( SubmarineModel, SubmarineInstanceMatrix );
	ModelInstance->UpdateTexture( TmpTextureDescriptor );

	GAME_SUBOBJECT_INSTANCE *IslandInstance = new GAME_SUBOBJECT_INSTANCE( IslandModel, IslandInstanceMatrix );
	TmpTextureDescriptor.textureGLid = TextureIsland;
	IslandInstance->UpdateTexture( TmpTextureDescriptor );

	GAME_SUBOBJECT_INSTANCE *WaterInstance = new GAME_SUBOBJECT_INSTANCE( WaterModel, WaterInstanceMatrix );
	TmpTextureDescriptor.textureGLid = TextureWater;
	WaterInstance->UpdateTexture( TmpTextureDescriptor );

	SUBOBJECT_WITH_CONTROLLER SubobjectWithController( ModelInstance, NULL );
	GAME_OBJECT* FirstGameObject = new GAME_OBJECT();
	FirstGameObject->models = std::vector<SUBOBJECT_WITH_CONTROLLER>( 1, SubobjectWithController );
	FirstGameObject->worldMatrix = glm::mat4(1.0f);
	glm::vec4 Sphere;
	CalculateGabarits( (*SubmarineSmdModel), SCALE_SUBMARINE_FACTOR, Sphere, FirstGameObject->firstCorner, FirstGameObject->secondCorner );
	FirstGameObject->sphereCenter = glm::vec3(Sphere);
	FirstGameObject->sphereRadius = Sphere.w;

	SUBOBJECT_WITH_CONTROLLER SubobjectWithController2( IslandInstance, NULL );
	GAME_OBJECT* FirstGameObject2 = new GAME_OBJECT();
	FirstGameObject2->models = std::vector<SUBOBJECT_WITH_CONTROLLER>( 1, SubobjectWithController2 );
	FirstGameObject2->worldMatrix = glm::mat4(1.0f);
	CalculateGabarits( (*IslandSmdModel), SCALE_ISLAND_FACTOR, Sphere, FirstGameObject2->firstCorner, FirstGameObject2->secondCorner );
	FirstGameObject2->sphereCenter = glm::vec3(Sphere);
	FirstGameObject2->sphereRadius = Sphere.w;

	SUBOBJECT_WITH_CONTROLLER SubobjectWithControllerWater( WaterInstance, NULL );
	GAME_OBJECT* WaterGameObject = new GAME_OBJECT();
	WaterGameObject->models = std::vector<SUBOBJECT_WITH_CONTROLLER>( 1, SubobjectWithControllerWater );
	WaterGameObject->worldMatrix = glm::mat4(1.0f);
	CalculateGabarits( (*WaterSmdModel), SCALE_WATER_FACTOR, Sphere, WaterGameObject->firstCorner, WaterGameObject->secondCorner );
	WaterGameObject->sphereCenter = glm::vec3(Sphere);
	WaterGameObject->sphereRadius = Sphere.w;

	//Create a game object template
	UnitFabric::AddTemplate( UNIT_TYPE_HEAVY_JET, UNIT_TEMPLATE_NAME, FirstGameObject );
	UnitFabric::AddTemplate( UNIT_TYPE_HEAVY_JET, UNIT_TEMPLATE_NAME2, FirstGameObject2 );
	UnitFabric::AddTemplate( UNIT_TYPE_HEAVY_JET, WATER_TEMPLATE_NAME, WaterGameObject );
	IUnit *Ship1, *Ship2, *Island, *Water;


	//Create units
	UNIT_PHYSIC_PARAMS PhysicParams;
	PhysicParams.initialPosition = glm::vec3( -8.0, 8.0, 4.0 );
	UnitFabric::CreateUnit( PhysicParams, UNIT_TEMPLATE_NAME, UNIT_NAME, &Ship1 );
	GameUnitsArray.push_back( Ship1 );
	glm::vec3 Gabarits[2];
	glm::vec3 Translation = Ship1->GetVisualInfo()->translation;
	FirstShipID = PhysicsEngine.AddRigidObject( 1.0,
												Ship1->GetVisualInfo()->orientation,
			                                    Translation,
			                                    Ship1->GetVisualInfo()->secondCorner,
			                                    glm::vec3( 0.0, 0.0, 0.0 ) );

	PhysicParams.initialPosition = glm::vec3( 2.0, -5.0, 4.0 );
	UnitFabric::CreateUnit( PhysicParams, UNIT_TEMPLATE_NAME, UNIT_NAME, &Ship2 );
	GameUnitsArray.push_back( Ship2 );
	Translation = Ship2->GetVisualInfo()->translation;
	SecondShipID = PhysicsEngine.AddRigidObject( 1.0,
												 Ship2->GetVisualInfo()->orientation,
												 Translation,
												 Ship2->GetVisualInfo()->secondCorner,
												 glm::vec3( 0.0, 0.0, 0.0 ) );

	PhysicParams.initialPosition = glm::vec3( -5.0, 3.0, 3.5 );
	UnitFabric::CreateUnit( PhysicParams, UNIT_TEMPLATE_NAME2, UNIT_NAME2, &Island );
	GameUnitsArray.push_back( Island );
	Translation = Island->GetVisualInfo()->translation;
	PhysicsEngine.AddRigidObject( 0.0,
								  Island->GetVisualInfo()->orientation,
								  Translation,
								  Island->GetVisualInfo()->secondCorner,
								  glm::vec3( 0.0, 0.0, 0.0 ) );

	PhysicParams.initialPosition = glm::vec3( -80.0, -80.0, 0.5 );
	UnitFabric::CreateUnit( PhysicParams, WATER_TEMPLATE_NAME, UNIT_NAME2, &Water );
	GameUnitsArray.push_back( Water );

	//Create water level plane
	PhysicsEngine.AddPlane( glm::vec3( 0.0f, 0.0f, 0.1f), 0.0f );

	fprintf( stderr, "Load models to OGL\n" );
    //Load models to OGL

	std::vector<PURE_COMPRESSED_MODEL> CompressedModels;
	PURE_COMPRESSED_MODEL CompressedModel( std::vector<STANDART_VERTEX> ( SubmarineSmdModel->vertices, SubmarineSmdModel->vertices +
			                                                              ( SubmarineSmdModel->vertexCount ) ),
										   std::vector<GLushort> ( SubmarineSmdModel->indexes, SubmarineSmdModel->indexes + ( SubmarineSmdModel->triangleCount * 3 ) ),
										   std::vector<GLuint> ( 1, TextureSubmarine )
										 );
	CompressedModel.renderInfo = SubmarineModel;

	PURE_COMPRESSED_MODEL CompressedModel2( std::vector<STANDART_VERTEX> ( IslandSmdModel->vertices, IslandSmdModel->vertices +
			                                                              ( IslandSmdModel->vertexCount ) ),
										   std::vector<GLushort> ( IslandSmdModel->indexes, IslandSmdModel->indexes + ( IslandSmdModel->triangleCount * 3 ) ),
										   std::vector<GLuint> ( 1, TextureIsland )
										 );
	CompressedModel2.renderInfo = IslandModel;

	PURE_COMPRESSED_MODEL CompressedGui  ( std::vector<STANDART_VERTEX> ( SmdGui->vertices, SmdGui->vertices +
			                                                              ( SmdGui->vertexCount ) ),
										   std::vector<GLushort> ( SmdGui->indexes, SmdGui->indexes + ( SmdGui->triangleCount * 3 ) ),
										   std::vector<GLuint> ( 1, 0 )
										 );
	CompressedGui.renderInfo = &GuiDummy;

	PURE_COMPRESSED_MODEL CompressedWater( std::vector<STANDART_VERTEX> ( WaterSmdModel->vertices, WaterSmdModel->vertices +
			                                                              ( WaterSmdModel->vertexCount ) ),
										   std::vector<GLushort> ( WaterSmdModel->indexes, WaterSmdModel->indexes + ( WaterSmdModel->triangleCount * 3 ) ),
										   std::vector<GLuint> ( 1, TextureWater )
										 );
	CompressedWater.renderInfo = WaterModel;

	CompressedModels.push_back( CompressedModel );
	CompressedModels.push_back( CompressedModel2 );
	CompressedModels.push_back( CompressedGui );
	CompressedModels.push_back( CompressedWater );

	VertexArrayId = Visualisator->SetUpModels( CompressedModels );

	//Start visualisation test
	Condition = true;
	RenderRun = new boost::thread( boost::bind( FirstRenderTest ) );

	fprintf( stderr, "StartVisualisation\n" );
	Visualisator->StartVisualisation( &FrameQueue, &InputMessageQueue );

    Condition = false;
    RenderRun->join();
	return 0;
}
